#include <Core/Version.h>
#include <Core/Box/BoxInstance.h>
#include <Core/DefaultCommandScheduler.h>
#include <Core/SearchEngine/MapKeyValueStorage.h>

#include <iostream>
#include <boost/program_options.hpp>

#if defined(UNIX) || defined(__APPLE__)
#	include <signal.h>
#elif defined(WIN32)
#	if !defined(WIN32_LEAN_AND_MEAN)
#		define WIN32_LEAN_AND_MEAN  
#	endif
#	include <windows.h>
#endif

#if defined(__APPLE__) || defined(UNIX)
void sigHandler(int signo);
#elif defined(WIN32)
BOOL __stdcall consoleHandler(DWORD reason_type);
#endif

BoxCore::ITransportLayerPtr transport;
BoxCore::BoxInstance * server = NULL;

void die(std::string const & message, int code);

void warning(std::string const & message);

void runBox(Core::DefaultCommandSchedulerParams commandSchedulerParams,
	BoxCore::BoxAsioTransportParams transportParams, BoxCore::BoxInstanceParams boxInstanceParams);

bool enableSigHandling(); 

int main(int argc, char ** argv) 
{
	std::cout << "Search box server [version 0.1][libCore version " 
		<< SIGSCAN_CORE_VERSION_STRING << "]" << std::endl;

	namespace po = boost::program_options;
	try 
	{
		std::string dbPath;
		Core::DefaultCommandSchedulerParams commandSchedulerParams = { 0 };
		BoxCore::BoxAsioTransportParams transportParams = { "", 0, 0 };
		BoxCore::BoxInstanceParams boxInstanceParams = { "", true };
		po::variables_map vm;
		po::options_description desc("Allowed options");
			desc.add_options()
		    ("help", "produce help message") 
				("box_name", po::value<std::string>(&boxInstanceParams.name)->default_value("server_slave_n"), 
					"server instance name")	
				("host", po::value<std::string>(&transportParams.host)->default_value("127.0.0.1"), 
					"host")	
				("port", po::value<int>(&transportParams.port)->default_value(5999), 
					"port")
				("max_sessions", po::value<int>(&transportParams.maxSessions)->default_value(1000), 
					"trasnport max sessions [NOT IMPLEMENTED]")
				("command_scheduler_concurrency", po::value<int>(&commandSchedulerParams.concurrency)->default_value(10), 
					"command scheduler workers numbers")
				("plain_text_db", po::value(&dbPath)->default_value("./text_db.txt"))
				;
		
		Core::initDefaultMapKeyValueStorage(Core::KeyValueStorageParams(), dbPath);

		if (!enableSigHandling())
			warning("Sig handling disabled");

		po::store(po::parse_command_line(argc, argv, desc), vm);
		po::notify(vm);  
	
		if (vm.count("help")) {
			std::cout << desc << std::endl;
			std::exit(0);
		}

		runBox(commandSchedulerParams, transportParams, boxInstanceParams); 
	}
	catch (std::exception const & expt) 
	{	
		die(expt.what(), -1);
	}
	return 0;
}

/**
 * Helpers
 */
void die(std::string const & message, int code) 
{
	std::cerr << "Error occured : " << message << " ,code : " << code << std::endl;
	std::exit(code);
}

void warning(std::string const & message) 
{ 
	std::cerr << "Warning : " << message << std::endl;
}

void runBox(Core::DefaultCommandSchedulerParams commandSchedulerParams,
	BoxCore::BoxAsioTransportParams transportParams, BoxCore::BoxInstanceParams boxInstanceParams)
{
	Core::initializeDefaultCommandScheduler(commandSchedulerParams);
	transport.reset(new BoxCore::BoxAsioTransport(transportParams));

	server = new BoxCore::BoxInstance(boxInstanceParams);
	server->initialize(transport, Core::defaultScheduler());

	server->startInstance();
	server->waitInstance();
}  

bool enableSigHandling() 
{
#if defined(UNIX) || defined(__APPLE__)
	if (signal(SIGINT, sigHandler) == SIG_ERR) 
		return false;
#elif defined(WIN32)
	if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE)
		return false
#else
#endif
	return true;
}

inline void atExit() 
{
	server->interruptInstance();
	delete server;
}

#if defined(UNIX) || defined(__APPLE__)
void sigHandler(int signo)
{
	if (signo != SIGINT) 
		return;
	atExit();
	std::cout << "Press 'enter' for exit..." << std::endl;
}
#elif defined(WIN32)
BOOL __stdcall consoleHandler(DWORD reason_type) 
{
	switch (reason_type) 
	{
		case CTRL_C_EVENT: case CTRL_BREAK_EVENT: case CTRL_CLOSE_EVENT: 
		case CTRL_SHUTDOWN_EVENT: case CTRL_LOGOFF_EVENT: 
			atExit();
			std::cout << "Press 'enter' for exit..." << std::endl;
		default : break;
	} 
	return TRUE;
}
#endif

